#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>

#define MAX_PATH_LENGTH 260
#define SCADE_EXE "VCS.exe"
#define ATTACH_DLL "ScadeAttach.dll"
#define HACK_BITBLT "hackBitBlt"
#define HACK_BITBLT_OFFSET 0x1F60
#define BE_ATTACH_DLL "etov.dll"
#define BE_ATTACH_CODE_OFFSET 0x3E73


class ProcessUtils {
	public:
		static MODULEENTRY32* GetModuleEntry(DWORD processId, const char* moduleName) {
		    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);
		
			MODULEENTRY32* me32 = (MODULEENTRY32*)malloc(sizeof(MODULEENTRY32));
			memset(me32, 0, sizeof(MODULEENTRY32));
		    if (hSnapshot == INVALID_HANDLE_VALUE) {
		        return me32;
		    }
		    me32 -> dwSize = sizeof(MODULEENTRY32);
		
		    if (Module32First(hSnapshot, me32)) {
		        do {
		            if (strcasecmp(me32 -> szModule, moduleName) == 0) {
		                CloseHandle(hSnapshot);
		                return me32;
		            }
		        } while (Module32Next(hSnapshot, me32));
		    }
		    CloseHandle(hSnapshot);
		    memset(me32, 0, sizeof(MODULEENTRY32));
		    return me32;
		}
		
		static DWORD GetModuleBaseAddress(DWORD processId, const char* moduleName) {
			MODULEENTRY32* entry = GetModuleEntry(processId, moduleName);
			DWORD baseAddr = (DWORD)(entry -> modBaseAddr);
			free(entry);
			return baseAddr;
		}
		
		static HMODULE GetModule(DWORD processId, const char* moduleName) {
			MODULEENTRY32* entry = GetModuleEntry(processId, moduleName);
			HMODULE module = entry -> hModule;
			free(entry);
			return module;
		}
		
		static bool ReadModuleCode(DWORD processId, PVOID addr, char* buffer, int size) {
		    HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
		    if (hProcess == NULL) {
		        printf("无法打开目标进程，错误代码: %lu\n", GetLastError());
		        return FALSE;
		    }
		
		    // 读取目标进程的内存
		    BOOL result = ReadProcessMemory(hProcess, addr, buffer, size, NULL);
		    if (result == FALSE) {
		        printf("读取内存失败，错误代码: %lu\n", GetLastError());
		        CloseHandle(hProcess);
		        return FALSE;
		    }
		    return TRUE;
		}
		
		static bool WriteModuleCode(DWORD processId, PVOID addr, char* buffer, int size) {
		    HANDLE hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, processId);
		    if (hProcess == NULL) {
		        printf("无法打开目标进程，错误代码: %lu\n", GetLastError());
		        return FALSE;
		    }
		
		    // 读取目标进程的内存
		    BOOL result = WriteProcessMemory(hProcess, addr, buffer, size, NULL);
		    if (result == FALSE) {
		        printf("写入内存失败，错误代码: %lu\n", GetLastError());
		        CloseHandle(hProcess);
		        return FALSE;
		    }
		    return TRUE;
		}
		
		static DWORD GetDLLFunctionAddress(DWORD processId, const char* dll, const char* func) {
			MODULEENTRY32* moduleEntry = ProcessUtils::GetModuleEntry(processId, dll);
			DWORD baseAddress = (DWORD) moduleEntry -> modBaseAddr;
			if (baseAddress == 0) {
				printf("无法找到模块：%s, 错误码为： %d\n", dll, GetLastError());
				return 0;
			}
			
			HMODULE hmodule = LoadLibrary(moduleEntry -> szExePath);
			if (hmodule == 0) {
				printf("无法加载拷贝模块：%s, 错误码为： %d\n", dll, GetLastError());
				return 0;
			}
			DWORD procAddr = (DWORD)GetProcAddress(hmodule, func);
			if (procAddr == 0) {
				printf("获取函数地址异常：%d\n", GetLastError());
			}
			free(moduleEntry);
			FreeLibrary(hmodule);
			return baseAddress + (procAddr - (DWORD) hmodule);
		}
};

class Utils {
	public:
		static BOOL GetAbsolutePathInCurrentFolder(const char* file, char* dst, int size) {
			char currentDirectory[MAX_PATH_LENGTH] = {'\0'};
		    // 获取当前工作目录
		    if (GetCurrentDirectory(MAX_PATH_LENGTH, currentDirectory) == 0) {
		        return FALSE;
		    }
			// 拼接 DLL 文件路径
			strcpy(dst, currentDirectory);
			// 检查当前目录路径末尾是否有路径分隔符
			if (dst[strlen(currentDirectory) - 1] != '\\') {
		        strcat(dst, "\\");
		    }
			strcat(dst, file);
			return TRUE;
		}
		
		static void HexPrint(char* buffer, int size) {
		    for (SIZE_T i = 0; i < size; i++) {
		        printf("%02X ", (unsigned char)buffer[i]);
		        if ((i + 1) % 16 == 0) {
		            printf("\n");
		        }
		    }
		    printf("\n");
		}
};


class Scade {
	private:
		const char* ProcessName = "VCS.exe";
		DWORD processID;
		DWORD etovDllBaseAddr;
	public:
		Scade():processID(0), etovDllBaseAddr(0) {}
		DWORD GetProcessId() {
			if (this -> processID > 0) return this -> processID;
		    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
		    if (hSnapshot == INVALID_HANDLE_VALUE) {
		        return 0;
		    }
		
		    PROCESSENTRY32 pe32;
		    pe32.dwSize = sizeof(PROCESSENTRY32);
		
		    // 获取第一个进程信息
		    if (Process32First(hSnapshot, &pe32)) {
		        do {
		            // 比较进程名
		            if (strcasecmp(pe32.szExeFile, this->ProcessName) == 0) {
		                CloseHandle(hSnapshot);
		                this -> processID = pe32.th32ProcessID;
		            }
		        } while (Process32Next(hSnapshot, &pe32));
		    }
		    CloseHandle(hSnapshot);
		    return this -> processID;
		}
		
		DWORD getETOVDllBaseAddr() {
			return this->etovDllBaseAddr > 0 ? this->etovDllBaseAddr : this->etovDllBaseAddr = ProcessUtils::GetModuleBaseAddress(this->GetProcessId(), "etov.dll");
		}
};


class Action {
	public:
		virtual BOOL Act() = 0;
		virtual BOOL UnAct() = 0;
};


class ScadeInjectDLLAction : public Action {
	public:
		BOOL Act() {
			return FALSE;
		}
		
		BOOL UnAct() {
			return FALSE;
		}
};


class ScadeETOVReplaceCodeAction : public Action {
	public:
		BOOL Act() {
			return FALSE;	
		}
		
		BOOL UnAct() {
			return FALSE;
		}
};


class Hacker {
	private:
		const char* DLL_NAME = "ScadeAttach.dll";
		const char* HACK_FUNCTION = "hackBitBlt";
		DWORD ETOV_CODE_OFFSET = 0x3E73;
		Scade* scade;
	public:
		Hacker(Scade* scade):scade(scade) {}
				
		BOOL InjectDLL(DWORD processId, const char* dllPath) {
		    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
		    if (hProcess == NULL) {
		        printf("无法打开目标进程，错误代码: %d\n", GetLastError());
		        return FALSE;
		    }
		
		    SIZE_T dllPathLen = strlen(dllPath) + 1;
		    LPVOID remoteDllPath = VirtualAllocEx(hProcess, NULL, dllPathLen, MEM_COMMIT, PAGE_READWRITE);
		    if (remoteDllPath == NULL) {
		        printf("无法在目标进程中分配内存，错误代码: %d\n", GetLastError());
		        CloseHandle(hProcess);
		        return FALSE;
		    }
		
		    if (!WriteProcessMemory(hProcess, remoteDllPath, dllPath, dllPathLen, NULL)) {
		        printf("无法将 DLL 路径写入目标进程，错误代码: %d\n", GetLastError());
		        VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
		        CloseHandle(hProcess);
		        return FALSE;
		    }
		
		    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
		    FARPROC loadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryA");
		    if (loadLibraryAddr == NULL) {
		        printf("无法获取 LoadLibraryA 函数地址，错误代码: %d\n", GetLastError());
		        VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
		        CloseHandle(hProcess);
		        return FALSE;
		    }
		
		    HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddr, remoteDllPath, 0, NULL);
		    if (hRemoteThread == NULL) {
		        printf("无法在目标进程中创建远程线程，错误代码: %d\n", GetLastError());
		        VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
		        CloseHandle(hProcess);
		        return FALSE;
		    }
		
		    WaitForSingleObject(hRemoteThread, INFINITE);
		    CloseHandle(hRemoteThread);
		    VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
		    CloseHandle(hProcess);
		    return TRUE;
		}
		
		DWORD GetETOVCodeAddr() {
			printf("开始获取ETOV的地址\n");
			DWORD etovDllBaseAddr = this->scade->getETOVDllBaseAddr();
			if (etovDllBaseAddr == 0) {
				printf("获取地址失败：%d", GetLastError());
				return 0;
			}
			return etovDllBaseAddr + this->ETOV_CODE_OFFSET;
		}
		
		DWORD injectFunction() {
			DWORD hackFuncAddr = ProcessUtils::GetDLLFunctionAddress(this->scade->GetProcessId(), this -> DLL_NAME, this->HACK_FUNCTION);	
			if (hackFuncAddr == 0) {
				printf("开始注入dll\n");
				char absPath[MAX_PATH_LENGTH] = {'\0'};
				Utils::GetAbsolutePathInCurrentFolder(this->DLL_NAME, absPath, MAX_PATH_LENGTH);
				if (!this->InjectDLL(scade->GetProcessId(), absPath)) {
					printf("注入dll失败：%d\n", GetLastError());
					return 0;
				}
				
				printf("获取dll函数地址\n");
				hackFuncAddr = ProcessUtils::GetDLLFunctionAddress(this->scade->GetProcessId(), this -> DLL_NAME, this->HACK_FUNCTION);	
				if (hackFuncAddr == 0) {
					printf("获取注入dll基址失败：%d\n", GetLastError());
					return 0;
				}
			} else {
				printf("注入的dll已存在，跳过注入\n");
			}
			return hackFuncAddr;
		}
		
		void uninjectFunction() {
			HMODULE module = ProcessUtils::GetModule(this->scade->GetProcessId(), this -> DLL_NAME);
			if (module != NULL) {
				if (FreeLibrary(module)) {
					printf("退出dll成功\n");	
				} else {
					printf("退出失败：%d\n", GetLastError());	
				}
			} else {
				printf("未载入DLL，无需回退\n");
			}
		}
		
		DWORD replaeCallCode(DWORD etovCodeAddr, DWORD hackFuncAddr) {
			printf("开始替换代码, 代码地址：%x, 注入函数地址：%x\n", etovCodeAddr, hackFuncAddr);
			char originCode[6];
			ProcessUtils::ReadModuleCode(this->scade->GetProcessId(), (PVOID)etovCodeAddr, originCode, 6);
			printf("原有代码为：\n");
			Utils::HexPrint(originCode, 6);
			if (originCode[0] != 0xFF) return 0;
			char replaceCode[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
			replaceCode[0] = 0xE8;
			replaceCode[5] = 0x90;
			int offset = hackFuncAddr-(etovCodeAddr+5);
			for(int i=0;i<4;i++) {
				replaceCode[i+1] = (offset >> 8*i)&0x000000ff;
			}
			printf("替换代码为：\n");
			Utils::HexPrint(replaceCode, 6);
			return ProcessUtils::WriteModuleCode(this->scade->GetProcessId(), (PVOID)etovCodeAddr, replaceCode, 6);
		}
		
		void rollbackCode(DWORD etovCodeAddr) {
			char originCode[6];
			ProcessUtils::ReadModuleCode(this->scade->GetProcessId(), (PVOID)etovCodeAddr, originCode, 6);
			if (originCode[0] == 0xE8) {
				char replaceCode[] = {0x00, 0x00, 0x00, 0x00, 0x00};
			} else {
				printf("当前代码未修改\n");
			}
			return;
		}
		
		BOOL hack() {
			// 获取ETOV汇编片段地址
			DWORD etovCodeAddr = this -> GetETOVCodeAddr();
			if (etovCodeAddr <= 0) return FALSE;
			// 注入函数
			DWORD hackFuncAddr = this -> injectFunction();
			if (hackFuncAddr <= 0) return FALSE;
			// 替换代码
			return this -> replaeCallCode(etovCodeAddr, hackFuncAddr);
		}
		
		void unhack() {
			return;
			// 退出dll
			this -> uninjectFunction();
			// 回退代码
			DWORD etovCodeAddr = this -> GetETOVCodeAddr();
			if (etovCodeAddr <= 0) return;
			this -> rollbackCode(etovCodeAddr);
		}
};


int main() {
	Scade* scade = new Scade();
	Hacker* hacker = new Hacker(scade);
	hacker -> hack();
//	printf("%x\n", ProcessUtils::getDLLFunctionAddress(scade->getProcessId(), "ScadeAttach.dll", "hackBitBlt"));
}